package au.edu.unsw.editingtool.transformxml;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.UUID;

import org.apache.commons.lang.StringEscapeUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import au.edu.unsw.editingtool.fedora.FedoraFactory;
import au.edu.unsw.editingtool.global.EditingToolUtil;

public class ValetXmlCreator {

    private org.dom4j.Document valetDocument;
    private Element root;
    private FileWriter out;

    private Element formData;
    private Element attachments;
    
    public ValetXmlCreator() {

    }

    public void load(File xmlFile) throws DocumentException {
        SAXReader reader = new SAXReader();
        valetDocument = reader.read(xmlFile);
        this.root = valetDocument.getRootElement();
        this.formData = (Element) valetDocument.selectSingleNode("Formdata");

    }

    public Document getDocument() {
        return valetDocument;
    }

    private String escapeHtml(String s) {
        String htmlEscaped = StringEscapeUtils.escapeHtml(s);
        return htmlEscaped;
    }

    /**
     * This method creates a new document object (Valet XML Document Object)
     * 
     * @param nodeName (String) nodeName is the name you would like to assign to
     * the root node of the Valet XML Document Object.
     */
    public void createValetDocument(String nodeName) {
        this.valetDocument = DocumentHelper.createDocument();
        root = this.valetDocument.addElement(nodeName);
    }

    /**
     * This method creates a Formdata element to the root node of the Valet XML
     * Document Object
     */
    public void createFormDataElement() {
        if (valetDocument.selectSingleNode("//Formdata") == null) {
            formData = this.root.addElement("Formdata");
        }
    }

    /**
     * This method creates an Attachments element to the root node of the Valet
     * XML Document Object
     */
    public void createAttachmentsElement() {
        attachments = this.root.addElement("Attachments");
    }

    /**
     * This method adds an attachment element to the Valet XML and adds the
     * original filename and new given filename (attribute) Document Object. If
     * no Attachments (plural) element exists then this method will create it
     */
    public void addSingleAttachment(String filename, String attribute) {
        if (valetDocument.selectSingleNode("//Attachments") == null) {
            this.createAttachmentsElement();
        }
        Element singleAttachmentElement = this.attachments.addElement("Attachment");
        singleAttachmentElement.addAttribute("id", attribute);
        Element elementObject2 = singleAttachmentElement.addElement("original_filename");
        elementObject2.addText(this.escapeHtml(filename));

    }

    public void addSingleAttachment(String filename, String attribute,
        Hashtable<String, String> elementData) {
        if (valetDocument.selectSingleNode("//Attachments") == null) {
            this.createAttachmentsElement();
        }
        Element singleAttachmentElement = this.attachments.addElement("Attachment");
        singleAttachmentElement.addAttribute("id", attribute);
        Element elementObject2 = singleAttachmentElement.addElement("original_filename");
        elementObject2.addText(this.escapeHtml(filename));

        // Add extra data from form
        for (Enumeration e = elementData.keys(); e.hasMoreElements();) {
            String elementName = (String) e.nextElement();
            String elementContents = (String) elementData.get(elementName);
            Element elementObject = singleAttachmentElement.addElement(elementName);
            elementObject.addText(this.escapeHtml(elementContents));
        }
    }

    /**
     * This method adds new elements to the root node of the VALET XML Document
     * Object
     * 
     * @param elementHashtable The method accepts a Hashtable where the Key is
     * the Element name and the Value is the contents of the newly created
     * Element root key value key value key value key etc...
     * 
     */
    public void createChildElementsWithContentsUsingHashtable(
        Hashtable<String, String> elementHashtable) {
        for (Enumeration e = elementHashtable.keys(); e.hasMoreElements();) {
            String elementName = (String) e.nextElement();
            String elementContents = (String) elementHashtable.get(elementName);
            Element elementObject = root.addElement(elementName);
            elementObject.addText(this.escapeHtml(elementContents));
        }
    }

    /**
     * This method adds new elements NOT to the root of the VALET XML Document
     * Object but to a new Element that it creates using the "parentElementName"
     * parameter
     * 
     * @param elementHashtable The method accepts a Hashtable where the Key is
     * the Element name and the Value is the contents of the newly created
     * Element
     * @param parentElementName If you provide the String "formdata" or
     * "Formdata" as the parentElementName the Hashtables contents will be added
     * inside the Formdata element. If the Formdata element does not exist it
     * will be created.
     */
    public void createChildElementsWithContentsUsingHashtable(
        Hashtable<String, String> elementHashtable, String parentElementName) {
        if (parentElementName.equalsIgnoreCase("formdata")) {
            this.createFormDataElement();
            for (Enumeration e = elementHashtable.keys(); e.hasMoreElements();) {
                String elementName = (String) e.nextElement();
                String elementContents = (String) elementHashtable.get(elementName);
                Element elementObject = formData.addElement(elementName);
                elementObject.addText(this.escapeHtml(elementContents));
            }

        } else {

            Element newElement;
            newElement = root.addElement(parentElementName);
            for (Enumeration e = elementHashtable.keys(); e.hasMoreElements();) {
                String elementName = (String) e.nextElement();
                String elementContents = (String) elementHashtable.get(elementName);
                Element elementObject = newElement.addElement(elementName);
                elementObject.addText(this.escapeHtml(elementContents));
            }
        }
    }

    /**
     * This method adds a new Element to the root node of the VALET XML Document
     * Object
     * 
     * @param elementName The method accepts a String name for the Element
     * @param elementContents The method accepts a String for the contents
     * <root><elementName>elementContents</elementName> etc...
     */
    public void createChildElementWithContentUsingStrings(String elementName,
        String elementContents) {
        Element elementObject = root.addElement(elementName);
        elementObject.addText(this.escapeHtml(elementContents));
    }

    /**
     * This method adds a new Element NOT to the root node of the VALET XML
     * Document Object but to a new Element that is creates using the parameter
     * parentElementName
     * 
     * @param elementName The method accepts a String name for the Element
     * @param elementContents The method accepts a String for the contents
     * @param parentElementName This method accepts a String for the new Element
     * to be created <root><parentElementName><elementName>elementContents</
     * elementName></parentElementName>
     */
    public void createChildElementWithContentUsingStrings(String elementName,
        String elementContents, String parentElementName) {
        if (parentElementName.equalsIgnoreCase("formdata")) {
            this.createFormDataElement();
            Element elementObject = formData.addElement(elementName);
            elementObject.addText(this.escapeHtml(elementContents));
        } else {
            Element newElement;
            newElement = root.addElement(parentElementName);
            Element elementObject = newElement.addElement(elementName);
            elementObject.addText(this.escapeHtml(elementContents));
        }
    }

    /**
     * This method returns a unique session ID for the VALET object
     * 
     */
    public String createUniqueId() {
        return UUID.randomUUID().toString();
    }

    public void saveValetXmlDocument(String directoryName, String prefix, String suffix) throws IOException {
    	if (!new File(directoryName).exists()) {
    		if (!new File(directoryName).mkdir()) 
    			directoryName = System.getProperty("java.io.tmpdir");
    	}
    	
    	String file = EditingToolUtil.createTempFile(prefix, suffix, directoryName);
    	
    	out = new FileWriter(file);
        this.valetDocument.write(out);
        out.close();
    }
    
    public void serializeValetXmlDocument(String uuid, String directoryName,
        String valetPid) {

        try {
            String outputString = directoryName + "/" + valetPid + "-" + uuid;
            out = new FileWriter(outputString);

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            this.valetDocument.write(out);
            // System.out.println("Writing Zip file to local disk");
            System.out.println(directoryName + "/" + uuid);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            out.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
    
    public void fedoraUpdateMetadata(String fedoraAddress, String username, String password,
    						 String pid, String datstreamId, String xsl) {
        try {
            FedoraFactory fedora = new FedoraFactory(fedoraAddress, username, password);
            byte[] metadata = fedora.transformXML(valetDocument.asXML().getBytes("UTF-8"), xsl);
            // fedora.modifyDatastreamByValue(pid, datastreamID, new Str, label,
            // contentType, content, state, log)
            fedora.modifyDatastreamByValue(pid, datstreamId, new String[] {},
                    "updated metadata", "text/xml", metadata, "A", "change metadata");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    
    /*

    public void fedoraUpdate(String pid, String datstreamId, String dcpath) {
        try {
            FedoraFactory fedora = new FedoraFactory(
                "http://localhost:8080/fedora", "fedoraAdmin", "fedoraAdmin");
            byte[] dc = fedora.transformXML(valetDocument.asXML().getBytes(
                "UTF-8"), dcpath);
            // modify dc
            fedora.modifyDatastreamByValue(pid, "DC", new String[] {},
                "Changed Stage", "text/xml", dc, "A", "prashant");
            // modify valetxml
            fedora.modifyDatastreamByValue(pid, "VALETXML", new String[] {},
                "Changed Stage", "text/xml", valetDocument.asXML().getBytes(
                    "UTF-8"), "A", "prashant");
            // fedora.modifyDatastreamByValue(pid, datastreamID, new Str, label,
            // contentType, content, state, log)
            String label = fedora.getLabel(pid);
            if (label != null) {
                String newLabel = "";
                String state = "D";
                if ("valet stage1".equals(label)) {
                    newLabel = "valet stage2";
                } else if ("valet stage2".equals(label)) {
                    newLabel = "valet published";
                    state = "A";
                }
                fedora.setLabel(pid, newLabel, state);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public String fedoraCreate(String datstreamId, String dcpath) {
        try {
        	FedoraFactory fedora = new FedoraFactory(
                "http://localhost:8080/fedora", "fedoraAdmin", "fedoraAdmin");
            byte[] dc = fedora.transformXML(valetDocument.asXML().getBytes(
                "UTF-8"), dcpath);
            byte[] foxml = fedora.writeFoxml(datstreamId, "valet xml",
                valetDocument.asXML(), new String(dc), "valet stage1", "I");
            System.out.println("foxml=" + new String(foxml));
            String pid = fedora.ingestObject(foxml);
            System.out.println("new pid=" + pid);
            return pid;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
    */
}
